﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Classes
{
    public class GraphImpl : IGraphImpl
    {
        IList<City> cities;
        IList<Flight> flights;
        IList<City> citiesOfSpan;
        IList<Flight> flightsForSearch;
        IList<City> sequenceOfParentCities;
        IList<Flight> listOfFlights;
        IList<Flight> sequence;
        IList<City> theCitiesList;
        ListOfConnections listOfConnections;
        
        City theCityWeGoFrom;
        City theCityWeGoTo;



        IList<City> citiesInConnection;
        IList<Flight> flightsInConnection;










        public GraphImpl()
        {
            citiesOfSpan = new List<City>();
            flights = new List<Flight>();
            cities = new List<City>();
            flightsForSearch = new List<Flight>();
            sequenceOfParentCities = new List<City>();
            listOfFlights = new List<Flight>();
            sequence = new List<Flight>(); // GetARoutetoTheCity(City from, City to)
            theCitiesList = new List<City>(); //  public IList<City> GetCities(City city)

            citiesInConnection = new List<City>();
            flightsInConnection = new List<Flight>();
            listOfConnections = new ListOfConnections();





        }
        public bool DoesThisCityExist(String cityName)
        {
            bool searched;
            if (cities.SingleOrDefault(x => x.Name == cityName) != null)
            {
                 searched = true;
            }
            else
            {
                searched = false;
            }
            return searched;
        }

        public City GetCityByName(String cityName)
        {
           if(DoesThisCityExist(cityName) != false)
           {
               City city = cities.Single(x => x.Name == cityName);
               return city;
           }
            else
           {
               return null;
           }
        }
        public void SetTheCityFrom(City v)
        {
            theCityWeGoFrom = v;
        }
       
        public void SetTheCityTo(City v)
        {
            theCityWeGoTo= v;

        }
        public IList<City> ReturnCityList()
        {
            return cities;
        }
        public bool IsEmpty()
        {
            return cities.Count == 0;
        }


        
        public void PurifyConnection(City to)
        {

        }
        public void AddCity(City c)
        {
            cities.Add(c);
        }
        public void AddFlight(City from, City to, int noOfSeats, int id, DateTime dt, DateTime arrival, int price, string company)
        {	//PRE (to,from) is not contained in the graph

            from.AddFlight(to, noOfSeats, id, dt, arrival, price, company);
        }

        public bool ContainsCity(City city)
        {
            return cities.Contains(city);
        }

        public bool IsAdjacent(City startCity, City endCity)
        {
            bool isAdjacent = false;
            foreach (Flight f in startCity.Flights())
            {
                if (f.To == endCity)
                {
                    isAdjacent = true;
                }
            }
            return isAdjacent;

        }

        public IList<City> GetAdjacencies(City city)
        {
            IList<City> list = new List<City>();
            foreach (Flight f in city.Flights())
            {
                list.Add(f.To);
            }
            return list;

        }


        /* I don't know how to implement this shit and when to use it
         public IEnumerator<City> Cities()
         {
             throw new System.NotImplementedException();
         }
         */

        public int GetNoOfCities()
        {
            int i = cities.Count;
            return i;
        }

        public int GetNoOfFlights()
        {
            int numberOfFlights = 0;
            foreach (City c in this.cities)
            {

                numberOfFlights += c.Flights().Count;
            }
            return numberOfFlights;
        }
        public void Unmark()
        {
            for (int i = 0; i < cities.Count; i++)
                (cities[i]).Mark = false;
        }
        public void Bfs(City v)
        {
            Queue<City> q = new Queue<City>();
            v.Mark = true;
            q.Enqueue(v);
            int level = 0;
            while (q.Count != 0)
            {
                City w = q.Dequeue();
                Console.WriteLine(w.Name);

                if (w.Mark)
                {
                    level++;
                }

                Console.WriteLine("Level: " + level);
                IList<City> adj = GetAdjacencies(w);
                for (int i = 0; i < adj.Count; i++)
                {
                    City u = adj[i];
                    Console.WriteLine(u.Name);
                    if (!u.Mark)
                    {
                        u.Mark = true;
                        q.Enqueue(u);
                    }
                }
            }
        }
        public ListOfConnections SearchForConnections(City weGoFrom, City weGoTo)
    {
        IList<City> visited = new List<City>();
        visited.Add(weGoFrom);
        SetTheCityFrom(weGoFrom);
        SetTheCityTo(weGoTo);
        
      //  ListOfConnections listOfConnections = BreadthFirst(visited); //used to return this variable I changet it like this for testing
        return BreadthFirst(visited); 
    }
       
     
        public ListOfConnections BreadthFirst(IList<City> visited)
        {

            IList<City> adj = GetAdjacencies(visited.Last());
            
            foreach(City c in adj)
            {
                
                if (visited.Contains(c))
                {
                    
                    continue;
                }
                if (c.Equals(theCityWeGoTo))
                {
                    
                    visited.Add(c);
                   // connection.SetFlightList(flightsForConnection);
                   // listOfConnections.AddConnection(connection);
                  //  PrintPath(visited);

                    Connection connection = new Connection();
                    connection.MakeFlightsOfCities(visited);
                    
                    listOfConnections.AddConnection(connection);
                    
                    

                    City f = visited.Last();
                    visited.Remove(f);
                   // break; now it does actually work work
                }

               foreach(City x in adj)            
               if (visited.Contains(x) || x.Equals(theCityWeGoTo)) {

                continue;
            }
               
            visited.Add(c);
            BreadthFirst(visited);
            City last = visited.Last();
            visited.Remove(last);
        }
           
           return listOfConnections;
      }
        
        



        public void Clear()
        {
            cities = new List<City>();
        }

       

        public void Dfs(City v)//normal version
        {
            v.Mark = true;
            Console.WriteLine(v.Name + " " + v.Country);
            ICollection<City> adj = GetAdjacencies(v);
            foreach (City u in adj)
            {
                if (u.Mark != true)
                {
                    Dfs(u);
                }
            }
        
      
           
           

    }

    }
}